import inspect
import os
import glob
import sys

import logging

from parameter_system import Parameterizable

logging.basicConfig(filename="plugins.log", filemode="w", level=logging.DEBUG)

class Plugin(Parameterizable):
    """
    All plugins must inherit from this class.  If you want
    to sort plugins into different types, create bases classes
    that inherit from this class, inherit from those base classes,
    and then call load_plugins() with those base classes.
    """
    def __init__(self, *args, **kwargs):
        pass

def load_plugins(plugin_dir_path, super_cls):
    """
    Finds and loads all classes in modules in plugin_dir_path
    that subclass super_cls.  The class super_cls must be
    Plugin or a subclass of Plugin.
    """
    logging.info("Called load_plugins with directory \"" + plugin_dir_path + "\" and class \"" + str(super_cls) + "\"")
    if not os.path.exists(plugin_dir_path):
        raise Exception, "Could not find plugin directory \"" + \
            plugin_dir_path + "\". My current dir is \"" + os.getcwd() + "\"."

    if not issubclass(super_cls, Plugin):
        raise Exception, "Given super class \"" + str(super_cls) + \
            "\" is subclass of Plugin."

    # If plugin dir is not in sys.path (where Python looks for modules),
    # add it
    plugin_dir_path = os.getcwd() + os.sep + plugin_dir_path
    norm_plugin_dir_path = os.path.normpath(os.path.expandvars(plugin_dir_path))
    if norm_plugin_dir_path not in sys.path:
        sys.path.append(norm_plugin_dir_path)
        logging.info("Added directory to system path.")

    # find all py files in plugin dir
    # load each py file as module
    # find classes in each module of the desired type
    glob_str = plugin_dir_path + os.sep + "*.py"
    ABSOLUTE_IMPORT = 0
    found_classes = []
    logging.info("Finding py files")
    for module_flname in glob.iglob(glob_str):
        logging.info("Looking at \"" + module_flname + "\"")
        module_name = os.path.splitext(os.path.basename(module_flname))[0]
        # Make sure that the objects are:
        # 1) A class
        # 2) A subclass of the desired base class
        # 3) From the desired module rather than imported from elsewhere
        plugin_pred = lambda p: inspect.isclass(p) and issubclass(p, super_cls) and p.__module__ == module_name
        # avoid special modules
        if module_name == "__init__":
            logging.info("Skipping __init__")
            continue
        try:
            module = __import__(module_name, globals(), locals(), [], ABSOLUTE_IMPORT)
            logging.info("imported module")
            members = inspect.getmembers(module, plugin_pred)
            logging.info("Inspected module.  Found: " + ",".join(map(str, members)))
            found_classes.extend(members)
        except Exception as e:
            raise Exception, "Could not load module \"" + module_name + "\": " + str(e)

    # get plugin names and return as dictionary
    plugin_factory = {}
    logging.info("Creating dictionary of plugins and their names")
    for cls_name, cls in found_classes:
        name = cls.get_plugin_name()
        plugin_factory[name] = cls
        logging.info("Class " + str(cls) + " has plugin name " + name)

    return plugin_factory
        
        
